#include "NodeVisitor.h"

#include <iostream>
using namespace std;

ClassDeclarationNode* TypeMeta::string_declaration = 0x0;

void TypeLinkingVisitor::visit(RootNode*n){
	class_environment = n->class_environment;

	ClassDeclarationNode dn;
	dn.identifier = "String";

	//If it crashed here. It usually means that you didn't link the stdlibs
	TypeMeta::string_declaration = *class_environment["java.lang."].find(&dn);
}
void TypeLinkingVisitor::visit(PackageDeclarationNode*n) {
	Clean();

	current_package = n->identifier;

	//Make sure that this package doesn't clash with any other packages
	error |= PrefixResolves(current_package);

	n->DeleteChildren();
}
void TypeLinkingVisitor::visit(SingleTypeImportDeclarationNode*n){
	Clean();

	NameNode *name = (NameNode*)(*n)["Name"];
	string package = string(), typname;
	ClassDeclarationNode *type = 0x0;
	if (!name) {
		typname = ((LeafNode*)(*n)["identifier"])->lexeme;
	} else {
		for (vector<SP<ParseNode> >::iterator it = name->children.begin(); it < name->children.end() - 1; ++it)
			package.append(((LeafNode*)(*it)())->lexeme + '.');

		typname = ((LeafNode*)name->children.back()())->lexeme;
	}
	

	ClassDeclarationNode cdn;
	cdn.identifier = typname;
	map<string, set<ClassDeclarationNode*, compare_decls> >::iterator find_result = class_environment.find(package);

	//Make sure the class exists before we put it in
	if (find_result == class_environment.end()) error = true;
	else if (find_result->second.find(&cdn) == find_result->second.end()) error = true;

	if (error) return;

	//Name the type and map to its package. The same type can be reference multiple times, just no clashing
	if (single_type_imports[typname].insert(package).second && single_type_imports[typname].size() > 1) error = true;

	//Also make sure no qualified prefix resolves
	error |= PrefixResolves(package);

	n->DeleteChildren();
}
void TypeLinkingVisitor::visit(TypeImportOnDemandDeclarationNode*n){
	Clean();

	NameNode *name = (NameNode*)(*n)["Name"];
	string package;
	if (!name) package = ((LeafNode*)(*n)["identifier"])->lexeme + '.';
	else {
		for (vector<SP<ParseNode> >::iterator it = name->children.begin(); it < name->children.end(); ++it)
			package.append(((LeafNode*)(*it)())->lexeme + '.');
	}

	unsigned int packages_found = 0;
	for (map<string, set<ClassDeclarationNode*, compare_decls> >::iterator it = class_environment.begin(); it != class_environment.end(); ++it) {
		//Is the on demain package a prefix of this one? Include this one as well
		if (it->first.find(package) == 0) {
			type_on_demand_packages.insert(it->first);
			packages_found += 1;
		}
	}

	//The packages in a type import on demand must exist somewhere
	if (packages_found == 0) {
		error = true;
		return;
	}

	error |= PrefixResolves(package);

	n->DeleteChildren();
}
void TypeLinkingVisitor::visit(ClassDeclarationNode*n){
	Clean();

	//Make sure that no single type import clashes with us before continuing (the single type imports don't clash with each other, by this point)
	for (map<string, set<string> >::iterator it = single_type_imports.begin(); it != single_type_imports.end(); ++it) {
			error |= it->first == n->identifier && *it->second.begin() != n->package_name;
	}

	if (error) return;

	//Put in the default includes
	for (map<string, set<ClassDeclarationNode*, compare_decls> >::iterator it = class_environment.begin(); it != class_environment.end(); ++it) {
		if (it->first.find("java.lang.") == 0) type_on_demand_packages.insert(it->first);
	}

	current_class = n;
	TypedNode *tn = (TypedNode*)(*n)["ClassType"];
	//If this class doesn't extend another class, then we extend java.lang.Object
	if (!tn) {
		//but only if this class isn't java.lang.Object
		if (n->package_name != "java.lang." || n->identifier != "Object") {
			tn = new TypedNode();
			tn->type_meta.category = TypeMeta::Reference;
			tn->type_package_name = "java.lang.";
			tn->type_name = "Object";
			visit(tn);
			n->super = (ClassDeclarationNode*)tn->type_meta.reference;
			delete tn;
			n->next_environment = n->super;
		}
	} else {
		visit(tn);
		n->super = (ClassDeclarationNode*)tn->type_meta.reference;
		n->next_environment = n->super;
	}

	if (n->super) {
		if(error |= (n->super->modifier & ModifiedNode::Final) != 0x0) return;
	}

	for (vector<SP<ParseNode> >::iterator it = n->children.begin(); it < n->children.end(); ++it) {
		if ((**it).Type() == "InterfaceType") {
			visit((TypedNode*)(*it)());
			//Checks for immediate duplicate interfaces
			error |= !n->interfaces.insert((InterfaceDeclarationNode*)((TypedNode*)(*it)())->type_meta.reference).second;
		}
	}	
	if (error) return;
	//n->DeleteOfType("ClassType");
	//n->DeleteOfType("InterfaceType");

	//Apply consistent data, even though these are redundant or useless
	n->type_package_name = n->package_name;
	n->type_name = n->identifier;
	n->type_meta.category = TypeMeta::Reference;
	n->type_meta.reference = n;

	local_needs_clearing = true;
}
void TypeLinkingVisitor::visit(InterfaceDeclarationNode*n) {
	visit((ClassDeclarationNode*)n);
}
void TypeLinkingVisitor::visit(ConstructorDeclarationNode*n){
	n->next_environment = current_class;
	current_method = n;

	//We now have linked types. Time to give this method a qualified method name
	n->identifier = n->identifier.substr(0, n->identifier.find_first_of('$')+1);
}


void TypeLinkingVisitor::visit(TypedNode *n) {
	if (!(n->type_meta.category & TypeMeta::Reference) || n->type_meta.reference) return;

		//Is it fully qualified?
	if (n->type_package_name.size() > 0) {

				string package_name = n->type_package_name;
		n->type_meta.reference = Get(package_name, n->type_name);

		if (n->type_meta.reference) {
						//No prefix resolves to a type in this environment
			error |= PrefixResolves(package_name);

			package_name = package_name.substr(0, package_name.find_first_of('.'));
			
			//The unqualified prefix also doesn't resolve
			error |= Get(package_name) != 0x0;
		}
	} else {
			
		n->type_meta.reference = Get(n->type_name);
		error |= n->type_meta.category == 0x0;
	}
	
	//reference has to exist and we have to be able to access it
	if (!n->type_meta.reference || (n->type_meta.reference->access == ModifiedNode::Protected && n->type_meta.reference->package_name != current_package)) {
		error |= true;
		return;
	}
	
		//Fill this in, now that we know it
	n->type_package_name = n->type_meta.reference->package_name;

	bool is_array = (n->type_meta.category & TypeMeta::Array) != 0x0;
	n->type_meta.category = (n->type_meta.reference == TypeMeta::string_declaration) ? TypeMeta::String : TypeMeta::Reference;

	if (is_array) n->type_meta.category = (TypeMeta::TypeCategory)(n->type_meta.category | TypeMeta::Array);
}

void TypeLinkingVisitor::visit(MethodDeclarationNode*n){
	n->next_environment = current_class;
	current_method = n;
	visit((TypedNode*)n);

	//We now have linked types. Time to give this method a qualified method name
	n->identifier = n->identifier.substr(0, n->identifier.find_first_of('$'));
}
void TypeLinkingVisitor::visit(AbstractMethodDeclarationNode*n){
	visit((MethodDeclarationNode*)n);
}
void TypeLinkingVisitor::visit(FieldDeclarationNode*n){
	visit((TypedNode*)n);
}
void TypeLinkingVisitor::visit(FormalParameterNode*n){
	visit((TypedNode*)n);
	((DeclarationNode*)n->parent)->identifier.append('$' + (n->type_meta.reference ? ((ClassDeclarationNode*)n->type_meta.reference)->package_name : "") + n->type_name + (n->type_meta.category & TypeMeta::Array ? "$" : ""));
}
void TypeLinkingVisitor::visit(LocalVariableDeclarationNode*n){
	visit((TypedNode*)n);
}
void TypeLinkingVisitor::visit(ClassTypeNode *n) {
	visit((TypedNode*)n);
}
void TypeLinkingVisitor::visit(InterfaceTypeNode *n) {
	visit((TypedNode*)n);
}
void TypeLinkingVisitor::visit(ClassInstanceCreationExpressionNode *n) {
	visit((TypedNode*)n);
}
void TypeLinkingVisitor::visit(ArrayCreationExpressionNode *n) {
	visit((TypedNode*)n);
}
void TypeLinkingVisitor::visit(CastExpressionNode *n) {
	visit((TypedNode*)n);
}
void TypeLinkingVisitor::visit(ReferenceTypeNode *n) {
	visit((TypedNode*)n);
}

//For ambiguous names. This tries to give a default type, if no prefix resolves to a closer expression declaration.
void TypeLinkingVisitor::visit(NameNode *n) {
	//Nothing found locally. Then we have to parse prefixes until we find a type
	if (!n->declaration_reference && !n->children.empty()) {
		unsigned int &pos = n->declaration_position;
		pos = 0; //Start at the beginning

		//Try to find the smallest package_name + name combo that resolves to a type
		string package_name = string(), name = ((LeafNode*)n->children[pos++]())->lexeme;
		n->declaration_reference = Get(name);

		//Until we've found a match, or there are no more prefixes available
		while(!n->declaration_reference && pos < n->children.size()) {
			package_name.append(name + '.');
			name = ((LeafNode*)n->children[pos++]())->lexeme;
			n->declaration_reference = Get(package_name, name);
		}

		//If we found something, it's obviously a type
		if (n->declaration_reference) n->declaration_namespace = DeclarationNode::Type;
	}
}


ClassDeclarationNode* TypeLinkingVisitor::Get(string package, string id) {
	ClassDeclarationNode dn;
	dn.identifier = id;
		map<string, set<ClassDeclarationNode*, compare_decls> >::iterator package_result = class_environment.find(package);
	if (package_result == class_environment.end()) return 0x0;

		
	set<ClassDeclarationNode*, compare_decls>::iterator declaration_result = package_result->second.find(&dn);
	if (declaration_result == package_result->second.end()) return 0x0;
	
	
	return *declaration_result;
}

ClassDeclarationNode* TypeLinkingVisitor::Get(string id) {
	if (current_class->identifier == id) return current_class;
	else {
		ClassDeclarationNode *ref = 0x0;
		//Is it part of single type import
		if (single_type_imports.find(id) != single_type_imports.end() && (ref = Get(*single_type_imports[id].begin(), id)) != 0x0) {}
		//Is it in the same package?
		else if ((ref = Get(current_package, id)) != 0x0) {}
		//Is it part of a type on demand?
		else {
			unsigned int num_results = 0;
			for (set<string>::iterator it = type_on_demand_packages.begin(); it != type_on_demand_packages.end(); ++it) {
				ClassDeclarationNode *loopref;
				if ((loopref = Get(*it, id)) != 0x0) {
					ref = loopref;
					++num_results;
				}
			}

			//Result must be unique
			if (num_results != 1) return 0x0;
		}

		return ref;
	}
	return 0x0;
}

bool TypeLinkingVisitor::PrefixResolves(string package_name) {
	string name;
	//check that no prefixes of the package are also types
	package_name.erase(package_name.end() - 1);
	for (size_t dot_pos = package_name.find_last_of('.'); !error && dot_pos != string::npos; dot_pos = package_name.find_last_of('.')) {
		name = package_name.substr(dot_pos + 1);
		package_name = package_name.substr(0, dot_pos + 1);

		if (Get(package_name, name) != 0x0) return true;

		package_name.erase(package_name.end() - 1);
	}

	return false;
}

void TypeLinkingVisitor::Clean() {
	if (local_needs_clearing) {
		single_type_imports.clear();
		type_on_demand_packages.clear();
		type_on_demand_packages.insert("java.lang.");
		current_package = string();
		local_needs_clearing = false;
	}
}